package model;

import java.awt.Color;
import java.awt.Rectangle;

import controller.Spiel;

public class Schritt {
	public final static Color LUECKE_COLOR = new Color(0, 0, 0, 0);
	public final static boolean JOIN = true;

	private float[][] coords;
	private Color color;
	private int timestamp;
	private float maxDist;
	private float middleX;
	private float middleY;
	private Schritt ignore;

	public static Schritt createJumpSchritt(Schritt last, float x, float y) {
		if (last != null)
			return new Schritt(last.coords[2][0], last.coords[2][1],
					last.coords[3][0], last.coords[3][1], x, y, x, y,
					LUECKE_COLOR);
		else
			return new Schritt(x, y, x, y, x, y, x, y, LUECKE_COLOR);
	}

	public static Schritt createSchritt(Schritt last, float phi, float v,
			float width, Color c) {
		float oldX1 = last.coords[2][0];
		float oldY1 = last.coords[2][1];
		float oldX2 = last.coords[3][0];
		float oldY2 = last.coords[3][1];

		float eX = (float) Math.cos(phi);
		float eY = (float) Math.sin(phi);
		float newX = last.getFinalX() + eX * v;
		float newY = last.getFinalY() + eY * v;

		float newX1 = newX + eY * width;
		float newY1 = newY - eX * width;
		float newX2 = newX - eY * width;
		float newY2 = newY + eX * width;

		Schritt s = new Schritt(oldX1, oldY1, oldX2, oldY2, newX1, newY1,
				newX2, newY2, c);
		s.setIgnoreSchritt(last);
		return s;
	}

	public Schritt(float oldX1, float oldY1, float oldX2, float oldY2,
			float newX1, float newY1, float newX2, float newY2, Color c) {
		// Evtl. Luecke
		this.color = c;
		this.coords = new float[4][2];
		this.coords[0][0] = oldX1;
		this.coords[0][1] = oldY1;
		this.coords[1][0] = oldX2;
		this.coords[1][1] = oldY2;
		this.coords[2][0] = newX1;
		this.coords[2][1] = newY1;
		this.coords[3][0] = newX2;
		this.coords[3][1] = newY2;
		this.timestamp = Spiel.CURRENT_SPIEL.getTime();

		computeBounds();
	}

	public void setIgnoreSchritt(Schritt s) {
		this.ignore = s;
	}

	private void computeBounds() {
		this.middleX = (this.coords[0][0] + this.coords[1][0]
				+ this.coords[2][0] + this.coords[3][0]) / 4;
		this.middleY = (this.coords[0][1] + this.coords[1][1]
				+ this.coords[2][1] + this.coords[3][1]) / 4;
		for (int i = 0; i < this.coords.length; i++) {
			for (int j = 0; j < this.coords.length; j++) {
				this.maxDist = (float) Math.max(
						this.maxDist,
						Math.sqrt((this.coords[i][0] - this.coords[j][0])
								* (this.coords[i][0] - this.coords[j][0])
								+ (this.coords[i][1] - this.coords[j][1])
								* (this.coords[i][1] - this.coords[j][1])));
			}
		}
	}

	public boolean overlapsWith(float x, float y, float radius) {
		for (int i = 0; i < this.coords.length; i++) {
			if ((this.coords[i][0] - x) * (this.coords[i][0] - x)
					+ (this.coords[i][1] - y) * (this.coords[i][1] - y) < radius
					* radius)
				return true;
		}
		return false;
	}

	public boolean overlapsWith(Schritt that) {
		if (this.color == LUECKE_COLOR || that.color == LUECKE_COLOR
				|| that == this.ignore || this == that.ignore)
			return false;

		if (Math.sqrt((this.middleX - that.middleX)
				* (this.middleX - that.middleX) + (this.middleY - that.middleY)
				* (this.middleY - that.middleY)) > this.maxDist + that.maxDist)
			return false;

		for (int i = 0; i < this.coords.length; i++) {
			for (int j = 0; j < that.coords.length; j++) {
				if (doLinesIntersect(this.coords[i][0], this.coords[i][1],
						this.coords[(i + 1) % this.coords.length][0],
						this.coords[(i + 1) % this.coords.length][1],
						that.coords[j][0], that.coords[j][1],
						that.coords[(j + 1) % that.coords.length][0],
						that.coords[(j + 1) % that.coords.length][1])) {
					return true;
				}
			}
		}
		return false;
	}

	private static boolean doLinesIntersect(float x11, float y11, float x12,
			float y12, float x21, float y21, float x22, float y22) {
		float m1 = x11 - x12 == 0 ? Integer.MAX_VALUE : (y11 - y12)
				/ (x11 - x12);
		float m2 = x21 - x22 == 0 ? Integer.MAX_VALUE : (y21 - y22)
				/ (x21 - x22);
		float t1 = y11 - m1 * x11;
		float t2 = y21 - m2 * x21;

		// Aufeinander
		if (t1 == t2 && m1 == m2)
			return false;
		// Parallel
		else if (m1 == m2)
			return false;

		float x = (t2 - t1) / (m1 - m2);
		float y = m1 * x + t1;

		return isInInterval(x11, x12, x) && isInInterval(x21, x22, x)
				&& isInInterval(y11, y12, y) && isInInterval(y21, y22, y);
	}

	private static boolean isInInterval(float a, float b, float test) {
		if (a < b)
			return a < test && test < b;
		return b < test && test < a;
	}

	public Color getColor() {
		return color;
	}

	public float[][] getCoords() {
		return coords;
	}

	public float getFinalWidth() {
		float xDiff = this.coords[2][0] - this.coords[3][0];
		float yDiff = this.coords[2][1] - this.coords[3][1];
		return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
	}

	public float getFinalX() {
		return (this.coords[2][0] + this.coords[3][0]) / 2;
	}

	public float getFinalY() {
		return (this.coords[2][1] + this.coords[3][1]) / 2;
	}

	public float getMiddleX() {
		return middleX;
	}

	public float getMiddleY() {
		return middleY;
	}

	public int getTimestamp() {
		return timestamp;
	}

	public Rectangle getBoundingRect() {
		int minX = Math.min((int) this.coords[0][0], Math.min(
				(int) this.coords[1][0],
				Math.min((int) this.coords[2][0], (int) this.coords[3][0])));
		int minY = Math.min((int) this.coords[0][1], Math.min(
				(int) this.coords[1][1],
				Math.min((int) this.coords[2][1], (int) this.coords[3][1])));
		int maxX = Math.max((int) this.coords[0][0], Math.max(
				(int) this.coords[1][0],
				Math.max((int) this.coords[2][0], (int) this.coords[3][0])));
		int maxY = Math.max((int) this.coords[0][1], Math.max(
				(int) this.coords[1][1],
				Math.max((int) this.coords[2][1], (int) this.coords[3][1])));
		return new Rectangle(minX, minY, maxX - minX, maxY - minY);
	}
}
